BemÀstra User Timing API för att skapa meningsfulla prestandamÄtt. Hitta flaskhalsar och optimera anvÀndarupplevelsen utöver vanliga web vitals.
BemÀstra frontend-prestanda: En djupdykning i User Timing API
I det moderna digitala landskapet Àr frontend-prestanda inte en lyx; det Àr ett grundlÀggande krav för framgÄng. För en global publik kan en lÄngsam, icke-responsiv webbplats leda till frustration hos anvÀndarna, minskat engagemang och en direkt negativ inverkan pÄ affÀrsresultaten. Vi har utmÀrkta standardiserade mÀtvÀrden som Core Web Vitals (Largest Contentful Paint, First Input Delay, Cumulative Layout Shift) som ger oss en grundlÀggande förstÄelse för anvÀndarupplevelsen. Men dessa mÀtvÀrden, Àven om de Àr avgörande, berÀttar bara en del av historien.
Hur Àr det med prestandan för applikationsspecifika funktioner? Hur lÄng tid tar det för sökresultaten att visas efter att en anvÀndare har skrivit en sökfrÄga? Hur mycket tid tar din komplexa datavisualiseringskomponent att rendera efter att ha mottagit data frÄn ett API? Hur pÄverkar en ny funktion hastigheten pÄ din single-page-applikations (SPA) sidövergÄngar? StandardmÀtvÀrden kan inte svara pÄ dessa detaljerade, affÀrskritiska frÄgor. Det Àr hÀr User Timing API kommer in i bilden, vilket ger utvecklare möjlighet att skapa anpassade, högprecisionsmÀtningar av prestanda som Àr skrÀddarsydda för deras unika applikationer.
Denna omfattande guide kommer att gÄ igenom allt du behöver veta för att utnyttja User Timing API, frÄn de grundlÀggande koncepten med mÀrken och mÀtningar till avancerade tekniker med PerformanceObserver. NÀr du Àr klar kommer du att vara rustad att gÄ bortom generiska mÀtvÀrden och börja berÀtta din applikations unika prestandahistoria.
Vad Àr Performance API? En bredare kontext
Innan vi djupdyker i User Timing Àr det viktigt att förstÄ att det Àr en del av en större uppsÀttning verktyg som gemensamt kallas Performance API. Detta webblÀsar-API ger tillgÄng till timingdata med hög precision relaterad till navigering, resursladdning och mer. Det globala objektet `window.performance` Àr din ingÄngspunkt till denna kraftfulla verktygslÄda.
Performance API bestÄr av flera grÀnssnitt, inklusive:
- Navigation Timing: Ger detaljerad tidsinformation om dokumentets navigeringsprocess, sÄsom tid som spenderats pÄ DNS-uppslag, TCP-handskakningar och mottagande av den första byten.
- Resource Timing: Erbjuder detaljerad nÀtverkstidsdata för varje resurs som laddas av sidan, inklusive bilder, skript och CSS-filer.
- Paint Timing: Exponerar tider för First Paint och First Contentful Paint.
- User Timing: Fokus för vÄr artikel, som lÄter utvecklare skapa sina egna anpassade tidsstÀmplar (mÀrken) och mÀta varaktigheten mellan dem (mÀtningar).
Dessa API:er arbetar tillsammans för att ge en helhetsbild av din applikations prestanda. VÄrt mÄl idag Àr att bemÀstra User Timing-delen, vilket ger oss kraften att lÀgga till vÄra egna anpassade kontrollpunkter i denna prestandatidslinje.
GrundlÀggande koncept: MÀrken och mÀtningar
User Timing API Àr bedrÀgligt enkelt och kretsar kring tvÄ grundlÀggande koncept: mÀrken och mÀtningar. TÀnk pÄ det som att anvÀnda ett stoppur. Du trycker pÄ en knapp för att markera en starttid, och du trycker pÄ den igen för att markera en sluttid. Varaktigheten mellan dessa tvÄ tryck Àr din mÀtning.
Skapa prestandamÀrken: `performance.mark()`
Ett 'mÀrke' Àr en namngiven tidsstÀmpel med hög upplösning som registreras vid en specifik tidpunkt i din applikations körning. Det Àr som att plantera en flagga pÄ din prestandatidslinje. Du kan skapa sÄ mÄnga mÀrken som du behöver för att identifiera nyckelmoment i en anvÀndarresa eller en komponents livscykel.
Syntaxen Àr enkel:
performance.mark(markName, [markOptions]);
markName: En strÀng som representerar det unika namnet för ditt mÀrke. VÀlj beskrivande namn!markOptions(valfritt): Ett objekt som kan innehÄlla endetail-egenskap för att bifoga extra metadata, och enstartTimeför att specificera en anpassad tidsstÀmpel.
GrundlÀggande exempel: Markera en hÀndelse
LÄt oss sÀga att vi vill markera början pÄ ett viktigt funktionsanrop.
function processLargeDataset() {
// SÀtt en flagga precis innan det tunga arbetet börjar
performance.mark('processLargeDataset:start');
// ... tung berÀkningslogik ...
console.log('Dataset processing complete.');
// SÀtt en annan flagga nÀr det Àr klart
performance.mark('processLargeDataset:end');
}
processLargeDataset();
I det hÀr exemplet har vi skapat tvÄ tidsstÀmplar i webblÀsarens prestandatidslinje: `processLargeDataset:start` och `processLargeDataset:end`. Just nu Àr de bara tidpunkter. Deras sanna kraft frigörs nÀr vi anvÀnder dem för att skapa en mÀtning.
LĂ€gga till kontext med `detail`-egenskapen
Ibland rÀcker inte en tidsstÀmpel ensam. Du kanske vill inkludera extra kontext om vad som hÀnde vid den tidpunkten. `detail`-egenskapen Àr perfekt för detta. Den kan innehÄlla all data som kan klonas strukturellt (som objekt, arrayer, strÀngar, nummer).
FörestÀll dig att vi markerar starten pÄ en komponentrendering och vill veta hur mÄnga objekt den renderade.
function renderProductList(products) {
const itemCount = products.length;
performance.mark('ProductList:render:start', {
detail: {
itemCount: itemCount,
source: 'initial-load'
}
});
// ... komponentens renderingslogik ...
performance.mark('ProductList:render:end');
}
const sampleProducts = new Array(1000).fill(0);
renderProductList(sampleProducts);
Denna ytterligare kontext Àr ovÀrderlig nÀr man analyserar prestandadata senare. Du kan till exempel korrelera renderingstider med antalet objekt för att se om det finns ett linjÀrt eller exponentiellt samband.
Skapa prestandamÀtningar: `performance.measure()`
En 'mÀtning' fÄngar varaktigheten mellan tvÄ tidpunkter. Det Àr berÀkningen som talar om för dig "hur lÄng tid" nÄgot tog. Oftast mÀter du tiden mellan tvÄ av dina anpassade mÀrken.
Syntaxen har nÄgra variationer:
performance.measure(measureName, startMarkOrOptions, [endMark]);
measureName: En strÀng som representerar det unika namnet för din mÀtning.startMarkOrOptions(valfritt): En strÀng med namnet pÄ startmÀrket. Kan ocksÄ vara ett alternativobjekt med `start`, `end`, `duration` och `detail`.endMark(valfritt): En strÀng med namnet pÄ slutmÀrket.
GrundlÀggande exempel: MÀta en funktions varaktighet
LÄt oss bygga vidare pÄ vÄrt `processLargeDataset`-exempel och faktiskt mÀta hur lÄng tid det tog.
function processLargeDataset() {
performance.mark('processLargeDataset:start');
// ... tung berÀkningslogik ...
performance.mark('processLargeDataset:end');
// Skapa nu mÀtningen
performance.measure(
'processLargeDataset:duration',
'processLargeDataset:start',
'processLargeDataset:end'
);
}
processLargeDataset();
Efter att denna kod har körts kommer webblÀsarens prestandabuffert att innehÄlla en ny post med namnet `processLargeDataset:duration`. Denna post kommer att ha en `duration`-egenskap som innehÄller den exakta tiden i millisekunder som förflöt mellan start- och slutmÀrkena.
Avancerade mÀtningsscenarier
Metoden `measure()` Àr mycket flexibel. Du behöver inte alltid ange tvÄ mÀrken.
- FrÄn navigeringsstart till ett mÀrke: Du kan mÀta tiden frÄn nÀr sidnavigeringen startade till ett av dina anpassade mÀrken. Detta Àr otroligt anvÀndbart för att mÀta saker som "Tid till interaktiv komponent".
// MÀt frÄn navigeringsstart tills huvudkomponenten Àr redo performance.measure('timeToInteractiveHeader', 'navigationStart', 'headerComponent:ready'); - FrÄn ett mÀrke till nu: Om du utelÀmnar `endMark` kommer mÀtningen att berÀknas frÄn ditt `startMark` till den aktuella tiden.
// MÀt frÄn startmÀrket tills denna kodrad exekveras performance.measure('timeSinceDataRequest', 'api:fetch:start'); - AnvÀnda alternativobjektet: Du kan ocksÄ skicka ett konfigurationsobjekt för att definiera mÀtningen, vilket Àr anvÀndbart för att lÀgga till en `detail`-egenskap.
performance.measure('complexRender:duration', { start: 'complexRender:start', end: 'complexRender:end', detail: { renderType: 'canvas' } });
Ă tkomst och rensning av prestandaposter
Att skapa mÀrken och mÀtningar Àr bara halva striden. Du behöver ett sÀtt att hÀmta denna data för att analysera den. `performance`-objektet tillhandahÄller flera metoder för detta.
performance.getEntries(): Returnerar en array med alla prestandaposter i bufferten (inklusive resurs-timings, navigerings-timings, etc.).performance.getEntriesByType(type): Returnerar en array med poster av en specifik typ. Oftast kommer du att anvÀnda `performance.getEntriesByType('mark')` och `performance.getEntriesByType('measure')`.performance.getEntriesByName(name, [type]): Returnerar en array med poster med ett specifikt namn (och valfritt, en specifik typ).
Exempel: Logga mÀtningar till konsolen
// Efter att ha kört vÄra tidigare exempel...
const allMeasures = performance.getEntriesByType('measure');
console.log(allMeasures);
// Ett mÀtningsobjekt ser ut ungefÀr sÄ hÀr:
// {
// "name": "processLargeDataset:duration",
// "entryType": "measure",
// "startTime": 12345.67,
// "duration": 150.89
// }
const specificMeasure = performance.getEntriesByName('processLargeDataset:duration');
console.log(`Processing took: ${specificMeasure[0].duration}ms`);
Viktigt: Rensa prestandabufferten
WebblÀsarens prestandabuffert Àr inte oÀndlig. För att förhindra minneslÀckor och hÄlla dina mÀtningar relevanta Àr det en god praxis att rensa de mÀrken och mÀtningar du har skapat nÀr du Àr klar med dem.
performance.clearMarks([name]): Rensar alla mÀrken, eller endast mÀrken med det angivna namnet.performance.clearMeasures([name]): Rensar alla mÀtningar, eller endast mÀtningar med det angivna namnet.
Ett vanligt mönster Àr att hÀmta data, bearbeta eller skicka den, och sedan rensa den.
function analyzeAndClear() {
const myMeasures = performance.getEntriesByName('processLargeDataset:duration');
// Skicka myMeasures till en analystjÀnst...
sendToAnalytics(myMeasures);
// StÀda upp för att frigöra minne
performance.clearMarks('processLargeDataset:start');
performance.clearMarks('processLargeDataset:end');
performance.clearMeasures('processLargeDataset:duration');
}
Praktiska, verkliga anvÀndningsfall för User Timing
Nu nÀr vi förstÄr mekaniken, lÄt oss utforska hur man tillÀmpar User Timing API för att lösa verkliga prestandautmaningar. Dessa exempel Àr ramverksoberoende och kan anpassas till vilken frontend-stack som helst.
1. MÀta varaktighet för API-anrop
Att förstÄ hur lÀnge din applikation vÀntar pÄ data Àr avgörande. Du kan enkelt linda in din datahÀmtningslogik med mÀrken och mÀtningar.
async function fetchUserData(userId) {
const markStart = `api:getUser:${userId}:start`;
const markEnd = `api:getUser:${userId}:end`;
const measureName = `api:getUser:${userId}:duration`;
performance.mark(markStart);
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error('Network response was not ok');
}
return await response.json();
} catch (error) {
console.error('Fetch error:', error);
// Du kan till och med lÀgga till detaljer om fel!
performance.mark(markEnd, { detail: { status: 'error', message: error.message } });
} finally {
// SÀkerstÀll att slutmÀrket och mÀtningen alltid skapas
if (performance.getEntriesByName(markEnd).length === 0) {
performance.mark(markEnd, { detail: { status: 'success' } });
}
performance.measure(measureName, markStart, markEnd);
}
}
fetchUserData('123');
Detta mönster ger exakta tidsmÀtningar för varje API-anrop, vilket gör att du kan identifiera lÄngsamma endpoints direkt frÄn verklig anvÀndardata.
2. SpÄra komponenters renderingstider i SPA:er
För ramverk som React, Vue eller Angular Àr mÀtning av tiden det tar för en komponent att monteras och renderas ett primÀrt anvÀndningsfall. Detta hjÀlper till att identifiera komplexa komponenter som kan sakta ner din applikation.
Exempel med React Hooks:
import React, { useLayoutEffect, useEffect, useRef } from 'react';
function MyHeavyComponent({ data }) {
const componentId = useRef(`MyHeavyComponent-${Math.random()}`).current;
const markStartName = `${componentId}:render:start`;
const markEndName = `${componentId}:render:end`;
const measureName = `${componentId}:render:duration`;
// useLayoutEffect körs synkront efter alla DOM-mutationer.
// Det Àr den perfekta platsen att markera starten pÄ renderingsmÀtningen.
useLayoutEffect(() => {
performance.mark(markStartName);
}, []); // Körs endast vid första montering
// useEffect körs asynkront efter att renderingen har slutförts pÄ skÀrmen.
// Detta Àr ett bra stÀlle att markera slutet.
useEffect(() => {
performance.mark(markEndName);
performance.measure(measureName, markStartName, markEndName);
// Logga resultatet för demonstration
const measure = performance.getEntriesByName(measureName)[0];
if (measure) {
console.log(`${measureName} took ${measure.duration}ms`);
}
// Rensa upp
performance.clearMarks(markStartName);
performance.clearMarks(markEndName);
performance.clearMeasures(measureName);
}, []); // Körs endast vid första montering
return (
// ... JSX för den tunga komponenten ...
);
}
3. Kvantifiera kritiska anvÀndarresor
Den mest effektfulla anvÀndningen av User Timing Àr att mÀta flerstegsinteraktioner frÄn anvÀndare som Àr kritiska för din verksamhet. Detta överskrider enkla tekniska mÀtvÀrden och mÀter den upplevda hastigheten hos din applikations kÀrnfunktionalitet.
TÀnk pÄ en utcheckningsprocess i en e-handel:
const checkoutButton = document.getElementById('checkout-btn');
checkoutButton.addEventListener('click', () => {
// 1. AnvÀndaren klickar pÄ 'kassa'-knappen
performance.mark('checkout:journey:start');
// ... kod för att validera varukorg, navigera till betalsida, etc. ...
});
// PÄ betalsidan, efter att betalningsformulÀret har renderats och Àr interaktivt
function onPaymentFormReady() {
performance.mark('checkout:paymentForm:ready');
performance.measure('checkout:timeToPaymentForm', 'checkout:journey:start', 'checkout:paymentForm:ready');
}
// Efter att betalningen har behandlats framgÄngsrikt och bekrÀftelseskÀrmen visas
function onPaymentSuccess() {
performance.mark('checkout:journey:end');
performance.measure('checkout:totalJourney:duration', 'checkout:journey:start', 'checkout:journey:end');
// Nu har du tvÄ kraftfulla mÀtvÀrden att analysera och optimera.
}
4. A/B-testa prestandaförbÀttringar
NÀr du refaktorerar en bit kod eller introducerar en ny algoritm, hur bevisar du att den faktiskt Àr snabbare för riktiga anvÀndare? User Timing ger objektiv data för A/B-testning.
FörestÀll dig att du har tvÄ olika sorteringsalgoritmer du vill testa:
function sortProducts(products, algorithmVersion) {
const markStart = `sort:v${algorithmVersion}:start`;
const markEnd = `sort:v${algorithmVersion}:end`;
const measureName = `sort:v${algorithmVersion}:duration`;
performance.mark(markStart);
if (algorithmVersion === 'A') {
// ... kör gammal sorteringsalgoritm ...
} else {
// ... kör ny, optimerad sorteringsalgoritm ...
}
performance.mark(markEnd);
performance.measure(measureName, markStart, markEnd);
}
// Baserat pÄ en A/B-testflagga skulle du anropa den ena eller den andra.
// Senare, i din analysdata, kan du jÀmföra den genomsnittliga varaktigheten för
// 'sort:vA:duration' mot 'sort:vB:duration' för att se vilken som var snabbare.
Visualisera och analysera dina anpassade mÀtvÀrden
Att skapa anpassade mÀtvÀrden Àr meningslöst om du inte analyserar datan. Det finns tvÄ primÀra sÀtt att nÀrma sig detta: lokalt under utveckling och aggregerat i produktion.
AnvÀnda webblÀsarens utvecklarverktyg
Moderna webblÀsare som Chrome och Firefox har utmÀrkt stöd för att visualisera User Timing-mÀrken och -mÀtningar i sina prestandaprofileringsverktyg.
- Ăppna din webblĂ€sares utvecklarverktyg (F12 eller Ctrl+Shift+I).
- GĂ„ till fliken Performance.
- Börja spela in en profil och utför sedan de ÄtgÀrder i din app som utlöser dina anpassade mÀrken och mÀtningar.
- Sluta spela in.
I tidslinjevyn hittar du en dedikerad rad som heter Timings. Dina anpassade mÀrken visas som vertikala linjer, och dina mÀtningar visas som fÀrgade staplar som visar deras varaktighet. Genom att hÄlla muspekaren över dem avslöjas deras namn och exakta tider. Detta Àr ett otroligt kraftfullt sÀtt att felsöka prestandaproblem under utveckling.
Skicka data till analys- och RUM-tjÀnster
För produktionsövervakning mÄste du samla in denna data frÄn dina anvÀndare och skicka den till en central plats för aggregering och analys. Detta Àr en central del av Real User Monitoring (RUM).
Det allmÀnna arbetsflödet Àr:
- Samla in de prestandamÀtningar du Àr intresserad av.
- Formatera dem till en lÀmplig payload (t.ex. JSON).
- Skicka payloaden till en analys-endpoint. Detta kan vara en tredjepartstjÀnst som Datadog, New Relic, Sentry, eller till och med Google Analytics (via anpassade hÀndelser), eller en anpassad backend som du kontrollerar.
function sendPerformanceData() {
// Vi bryr oss bara om vÄra anpassade applikationsmÀtningar
const appMeasures = performance.getEntriesByType('measure').filter(
(entry) => entry.name.startsWith('app:') // AnvÀnd en namnkonvention!
);
if (appMeasures.length > 0) {
const payload = JSON.stringify(appMeasures.map(measure => ({
name: measure.name,
duration: measure.duration,
startTime: measure.startTime,
details: measure.detail, // Skicka vÄr rika kontext
path: window.location.pathname // LĂ€gg till mer kontext
})));
// AnvÀnd navigator.sendBeacon för tillförlitlig, icke-blockerande datasÀndning
navigator.sendBeacon('https://analytics.example.com/performance', payload);
// Rensa upp de mÀtningar som har skickats
appMeasures.forEach(measure => {
performance.clearMeasures(measure.name);
// Rensa Àven de associerade mÀrkena
});
}
}
// Anropa denna funktion vid en lÀmplig tidpunkt, t.ex. nÀr sidan Àr pÄ vÀg att stÀngas ner
window.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
sendPerformanceData();
}
});
Avancerade tekniker och bÀsta praxis
För att verkligen bemÀstra User Timing API, lÄt oss titta pÄ nÄgra avancerade funktioner och bÀsta praxis som kommer att göra din instrumentering mer robust och effektiv.
AnvÀnda `PerformanceObserver` för asynkron övervakning
Metoderna `getEntries*()` krÀver att du manuellt avfrÄgar (poll) prestandabufferten. Detta har tvÄ nackdelar: du kan köra din kontroll för sent och missa poster om bufferten har fyllts och rensats, och sjÀlva avfrÄgningen kan ha en mindre prestandakostnad. Den moderna, föredragna lösningen Àr `PerformanceObserver`.
En `PerformanceObserver` lÄter dig prenumerera pÄ hÀndelser för prestandaposter. Din callback-funktion kommer att anropas asynkront nÀr nya poster av de typer du observerar registreras.
// 1. Skapa en callback-funktion för att hantera nya poster
const observerCallback = (list) => {
for (const entry of list.getEntries()) {
console.log('New measure observed:', entry.name, entry.duration);
// HÀr kan du omedelbart skicka posten till din analystjÀnst
// utan att behöva avfrÄga eller vÀnta.
}
};
// 2. Skapa observatörsinstansen
const observer = new PerformanceObserver(observerCallback);
// 3. Börja observera för posttyperna 'mark' och 'measure'
// Alternativet 'buffered: true' sÀkerstÀller att du fÄr poster som skapades
// *innan* observatören registrerades.
observer.observe({ entryTypes: ['mark', 'measure'], buffered: true });
// Nu, varje gÄng performance.mark() eller performance.measure() anropas nÄgonstans
// i din applikation, kommer observerCallback att utlösas med den nya posten.
// För att sluta observera senare:
// observer.disconnect();
Att anvÀnda `PerformanceObserver` Àr mer effektivt, mer tillförlitligt och bör vara ditt standardval för att samla in prestandadata i en produktionsmiljö.
Etablera en tydlig namnkonvention
NÀr din applikation vÀxer kommer du att samla pÄ dig mÄnga anpassade mÀtvÀrden. Utan en konsekvent namnkonvention blir din data svÄr att filtrera och analysera. Anta ett mönster som ger kontext.
En bra konvention kan vara: [appName]:[featureOrComponent]:[eventName]:[status]
ecom:ProductGallery:render:startecom:ProductGallery:render:endecom:ProductGallery:render:durationadmin:DataTable:fetchApi:startadmin:DataTable:fetchApi:duration
Denna struktur gör det trivialt att filtrera efter alla mÀtvÀrden relaterade till `ProductGallery` eller att hitta alla `fetchApi`-varaktigheter över hela applikationen.
Abstrahera till en verktygstjÀnst
För att sÀkerstÀlla konsekvens och minska repetitiv kod, slÄ in `performance`-anropen i din egen verktygsmodul eller tjÀnst. Detta gör det ocksÄ enkelt att aktivera eller inaktivera prestandaövervakning baserat pÄ miljön.
// performance-service.js
const IS_PERFORMANCE_MONITORING_ENABLED = process.env.NODE_ENV === 'production' || window.location.search.includes('perf=true');
export const perfMark = (name, options) => {
if (!IS_PERFORMANCE_MONITORING_ENABLED) return;
performance.mark(name, options);
};
export const perfMeasure = (name, start, end) => {
if (!IS_PERFORMANCE_MONITORING_ENABLED) return;
performance.measure(name, start, end);
};
export const startJourney = (name) => {
perfMark(`${name}:start`);
};
export const endJourney = (name) => {
const startMark = `${name}:start`;
const endMark = `${name}:end`;
const measureName = `${name}:duration`;
perfMark(endMark);
perfMeasure(measureName, startMark, endMark);
// Rensa eventuellt mÀrkena hÀr
};
// I din komponent:
// import { startJourney, endJourney } from './performance-service';
// startJourney('ecom:checkout');
// ...senare...
// endJourney('ecom:checkout');
Slutsats: Ta kontroll över din applikations prestandaberÀttelse
Ăven om standardmĂ€tvĂ€rden som Core Web Vitals ger en grundlĂ€ggande hĂ€lsokontroll för din webbplats, belyser de inte prestandan hos de funktioner och interaktioner som gör din applikation unik. User Timing API Ă€r bron som överbryggar denna klyfta. Det tillhandahĂ„ller en enkel men djupt kraftfull mekanism för att mĂ€ta det som verkligen betyder nĂ„got för dina anvĂ€ndare och ditt företag.
Genom att implementera anpassade mÀrken och mÀtningar omvandlar du prestandaoptimering frÄn ett gissningsspel till en datadriven vetenskap. Du kan peka ut de exakta funktionerna, komponenterna eller anvÀndarflödena som orsakar flaskhalsar, validera effekten av dina refaktoreringsinsatser med objektiva siffror, och i slutÀndan bygga en snabbare, trevligare upplevelse för din globala publik.
Börja i liten skala. Identifiera den enskilt mest kritiska anvĂ€ndarresan i din applikation â vare sig det Ă€r att söka efter en produkt, skicka ett formulĂ€r eller ladda en datainstrumentpanel. Instrumentera den med `performance.mark()` och `performance.measure()`. Analysera resultaten i dina utvecklarverktyg. NĂ€r du ser den klarhet det ger, kommer du att ha kraften att berĂ€tta din applikations fullstĂ€ndiga prestandaberĂ€ttelse, ett anpassat mĂ€tvĂ€rde i taget.